/* Copyright 2007-2008 dnAnalytics Project.
 *
 * Contributors to this file:
 * Patrick van der Velde
 * Marcus Cuda - http://marcus.cuda.net
 *
 * This file is part of dnAnalytics.  dnAnalytics is licensed under the 
 * Microsoft Public License. See License.txt for a complete copy of the
 * license.
 */


using System;
using System.Runtime.InteropServices;
using System.Text;

namespace dnAnalytics.Math
{
    ///<summary>A Complex32 number data type.</summary>
    ///<remarks>See <see cref="ComplexMath">Complex32Math</see> for Complex32 math functions.</remarks>
    ///<seealso cref="ComplexMath"/>
    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    public struct Complex32 : IFormattable, IEquatable<Complex32>
    {
        private static readonly Complex32 sI = new Complex32(0, 1);
        private static readonly Complex32 sInfinity = new Complex32(float.PositiveInfinity, float.PositiveInfinity);
        private static readonly Complex32 sNaN = new Complex32(float.NaN, float.NaN);
        private static readonly Complex32 sOne = new Complex32(1);
        private static readonly Complex32 sZero = new Complex32(0);

        private readonly float mReal;
        private readonly float mImag;

        /// <summary>
        /// Creates a Complex32 number from a real number (the imaginary component is set to
        /// <strong>zero</strong>).
        /// </summary>
        /// <param name="real">The real component of the Complex32 number.</param>
        public Complex32(float real) : this(real, 0) {}

        /// <summary>Creates a Complex32 number.</summary>
        /// <param name="real">The real component of the Complex32 number.</param>
        /// <param name="imaginary">The imaginary component of the Complex32 number.</param>
        public Complex32(float real, float imaginary)
        {
            mReal = real;
            mImag = imaginary;
        }

        /// <summary>
        /// Created a Complex32 number from the given string. The string can be in the
        /// following formats (without the quotes): 'n', 'ni', 'n +/- ni', 'n,n', 'n,ni,' '(n,n)',
        /// or '(n,ni)', where n is a real number.
        /// </summary>
        /// <exception cref="FormatException">if the n, is not a number.</exception>
        /// <exception cref="ArgumentNullException">if s, is <see langword="null" />.</exception>
        /// <param name="value">The string to create the Complex32 number from.</param>
        public Complex32(string value) : this(value, null) {}

        /// <summary>
        /// Created a Complex32 number from the given string. The string can be in the
        /// following formats (without the quotes): 'n', 'ni', 'n +/- ni', 'n,n', 'n,ni,' '(n,n)',
        /// or '(n,ni)', where n is a real number.
        /// </summary>
        /// <exception cref="FormatException">if the n, is not a number.</exception>
        /// <exception cref="ArgumentNullException">if s, is <see langword="null" />.</exception>
        /// <param name="value">The string to create the Complex32 number from.</param>
        /// <param name="formatProvider">An IFormatProvider that supplies culture-specific formatting information.</param>
        public Complex32(string value, IFormatProvider formatProvider)
        {
            this = Parse(value, formatProvider);
        }

        /// <summary>A constant value representing the number one as a Complex32 number.</summary>
        public static Complex32 One
        {
            get { return sOne; }
        }

        /// <summary>A constant value representing the number zero as a Complex32 number.</summary>
        public static Complex32 Zero
        {
            get { return sZero; }
        }

        /// <summary>A constant value representing not a number (NaN) as a Complex32 number.</summary>
        public static Complex32 NaN
        {
            get { return sNaN; }
        }

        /// <summary>A constant value representing infinity as a Complex32 number.</summary>
        public static Complex32 Infinity
        {
            get { return sInfinity; }
        }

        /// <summary>A constant value representing the Complex32 number I.</summary>
        public static Complex32 I
        {
            get { return sI; }
        }

        /// <summary>A read-only property to access the real component of Complex32 number.</summary>
        public float Real
        {
            get { return mReal; }
        }

        /// <summary>A read-only property to access the imaginary component of Complex32 number.</summary>
        public float Imaginary
        {
            get { return mImag; }
        }

        /// <summary>Returns the conjugate of a Complex32 number.</summary>
        public Complex32 Conjugate
        {
            get { return ComplexMath.Conjugate(this); }
        }

        /// <summary>Returns the Absolute of a Complex32 number.</summary>
        public float Absolute
        {
            get { return ComplexMath.Absolute(this); }
        }

        /// <summary>Implicitly converts a float to a Complex32 number.</summary>
        /// <returns>A Complex32 number containing the value of the given float.</returns>
        /// <param name="value">The float value to convert.</param>
        public static implicit operator Complex32(float value)
        {
            return new Complex32(value);
        }

        /// <summary>Converts a float to Complex32 number.</summary>
        /// <param name="value">The <strong>float</strong> value to convert.</param>
        public static Complex32 ToComplex32(float value)
        {
            return new Complex32(value);
        }

        /// <summary>
        /// Checks if two Complex32 numbers are equal. Two Complex32 numbers are equal if their
        /// corresponding real and imaginary components are equal.
        /// </summary>
        /// <returns>
        /// Returns true if the two objects are the same object, or if their corresponding
        /// real and imaginary components are equal, false otherwise.
        /// </returns>
        /// <param name="other">The Complex32 number to compare to with.</param>
        public bool Equals(Complex32 other)
        {
            return Real == other.Real && Imaginary == other.Imaginary;
        }

        /// <summary>A string representation of this Complex32 number.</summary>
        /// <returns>
        /// The string representation of this Complex32 number formatted as specified by the
        /// format string and format provider.
        /// </returns>
        /// <exception cref="FormatException">if the n, is not a number.</exception>
        /// <exception cref="ArgumentNullException">if s, is <see langword="null" />.</exception>
        /// <param name="format">A format specification.</param>
        /// <param name="formatProvider">An IFormatProvider that supplies culture-specific formatting information.</param>
        public String ToString(string format, IFormatProvider formatProvider)
        {
            if (IsNaN(this))
            {
                return "NaN";
            }
            if (IsInfinity(this))
            {
                return "Infinity";
            }

            StringBuilder ret = new StringBuilder();

            ret.Append(mReal.ToString(format, formatProvider));
            if (mImag < 0)
            {
                ret.Append(" ");
            }
            else
            {
                ret.Append(" + ");
            }
            ret.Append(mImag.ToString(format, formatProvider)).Append("i");

            return ret.ToString();
        }

        /// <summary>The hash code for the the Complex32 number.</summary>
        /// <returns>The hash code of the Complex32 number.</returns>
        /// <remarks>
        /// The hash code is calculated as
        /// System.Math.Exp(Complex32Math.Absolute(Complex32Number)).
        /// </remarks>
        public override int GetHashCode()
        {
            return (int) System.Math.Exp(ComplexMath.Absolute(this));
        }

        /// <summary>
        /// Checks if two Complex32 numbers are equal. Two Complex32 numbers are equal if their
        /// corresponding real and imaginary components are equal.
        /// </summary>
        /// <returns>
        /// Returns true if the two objects are the same object, or if their corresponding
        /// real and imaginary components are equal, false otherwise.
        /// </returns>
        /// <param name="obj">The Complex32 number to compare to with.</param>
        public override bool Equals(Object obj)
        {
            if (obj == null)
            {
                return false;
            }
            if (obj is Complex32)
            {
                Complex32 rightSide = (Complex32) obj;
                return Real == rightSide.Real && Imaginary == rightSide.Imaginary;
            }
            return false;
        }

        /// <summary>Equality operator to compare two Complex32 numbers.</summary>
        /// <remarks>Returns false if the two variables are not equals using Equals function</remarks>
        /// <returns>
        /// True if the corresponding real and imaginary components of the two Complex32
        /// numbers are equal; otherwise false.
        /// </returns>
        /// <param name="o1">One of the two Complex32 numbers to compare.</param>
        /// <param name="o2">The other Complex32 number to compare.</param>
        public static bool operator ==(Complex32 o1, Complex32 o2)
        {
            return o1.Equals(o2);
        }

        /// <summary>Inequality operator to compare a Complex32 numbers.</summary>
        /// <remarks>Returns false if the two variables are equal using Equals function</remarks>
        /// <returns>
        /// False if the corresponding real and imaginary components of the two Complex32
        /// numbers are equal; otherwise true.
        /// </returns>
        /// <param name="o1">One of the two Complex32 numbers to compare.</param>
        /// <param name="o2">The other Complex32 number to compare.</param>
        public static bool operator !=(Complex32 o1, Complex32 o2)
        {
            return !o1.Equals(o2);
        }

        /// <summary>
        /// Unary plus operator. Added for completion and just returns the same
        /// object.
        /// </summary>
        /// <returns>The same value that is passed to the method.</returns>
        /// <param name="value">The value to return.</param>
        public static Complex32 operator +(Complex32 value)
        {
            return value;
        }

        /// <summary>Unary plus. Added for completion and just returns the same object.</summary>
        /// <returns>The same value that is passed to the method.</returns>
        /// <param name="value">The value to return.</param>
        public static Complex32 Plus(Complex32 value)
        {
            return value;
        }

        /// <summary>Addition operator. Adds two Complex32 numbers together.</summary>
        /// <returns>The result of the addition.</returns>
        /// <param name="leftSide">One of the Complex32 numbers to add.</param>
        /// <param name="rightSide">The other Complex32 numbers to add.</param>
        public static Complex32 operator +(Complex32 leftSide, Complex32 rightSide)
        {
            return new Complex32(leftSide.Real + rightSide.Real, leftSide.Imaginary + rightSide.Imaginary);
        }

        /// <summary>Adds a complex number to this one.</summary>
        /// <returns>The result of the addition.</returns>
        /// <param name="other">The complex number to add to.</param>
        public Complex32 Add(Complex32 other)
        {
            return this + other;
        }

        /// <summary>Unary negation operator. Returns the negation of a Complex32 number.</summary>
        /// <returns>The negated value.</returns>
        /// <param name="value">The Complex32 number to negate.</param>
        public static Complex32 operator -(Complex32 value)
        {
            return new Complex32(-value.Real, -value.Imaginary);
        }

        /// <summary>Negates this complex number.</summary>
        /// <returns>The negated value.</returns>
        public Complex Negate()
        {
            return -this;
        }

        /// <summary>Subtraction operator. Subtracts two Complex32 numbers.</summary>
        /// <returns>The result of the subtraction.</returns>
        /// <param name="leftSide">The Complex32 number to subtract from.</param>
        /// <param name="rightSide">The Complex32 number to subtract.</param>
        public static Complex32 operator -(Complex32 leftSide, Complex32 rightSide)
        {
            return new Complex32(leftSide.Real - rightSide.Real, leftSide.Imaginary - rightSide.Imaginary);
        }

        /// <summary>Subtracts a complex number for this one.</summary>
        /// <returns>The result of the subtraction.</returns>
        /// <param name="other">The complex number to subtract.</param>
        public Complex32 Subtract(Complex32 other)
        {
            return this - other;
        }

        /// <summary>Multiplication operator. Multiplies two Complex32 numbers.</summary>
        /// <returns>The result of the multiplication.</returns>
        /// <param name="leftSide">One of the Complex32 numbers to multiply.</param>
        /// <param name="rightSide">The other Complex32 number to multiply.</param>
        public static Complex32 operator *(Complex32 leftSide, Complex32 rightSide)
        {
            return
                new Complex32(leftSide.Real*rightSide.Real - leftSide.Imaginary*rightSide.Imaginary,
                              leftSide.Real*rightSide.Imaginary + leftSide.Imaginary*rightSide.Real);
        }

        /// <summary>Multiplies a complex number with this one.</summary>
        /// <returns>The result of the multiplication.</returns>
        /// <param name="other">The complex number to multiply with.</param>
        public Complex32 Multiply(Complex32 other)
        {
            return this*other;
        }

        /// <summary>Division operator. Divides a Complex32 number by another.</summary>
        /// <returns>The result of the division.</returns>
        /// <param name="leftSide">The dividend.</param>
        /// <param name="rightSide">The division.</param>
        public static Complex32 operator /(Complex32 leftSide, Complex32 rightSide)
        {
            if (rightSide.mReal == 0.0 && rightSide.Imaginary == 0.0)
            {
                return sNaN;
            }

            if (IsInfinity(rightSide) && !IsInfinity(leftSide))
            {
                return sZero;
            }

            float real, imag;
            if (System.Math.Abs(rightSide.mReal) >= System.Math.Abs(rightSide.mImag))
            {
                float temp = rightSide.mImag/rightSide.mReal;
                float denom = rightSide.mReal + temp*rightSide.mImag;
                real = (leftSide.mReal + temp*leftSide.mImag)/denom;
                imag = (leftSide.mImag - temp*leftSide.mReal)/denom;
            }
            else
            {
                float temp = rightSide.mReal/rightSide.mImag;
                float denom = rightSide.mImag + temp*rightSide.mReal;
                real = (leftSide.mReal*temp + leftSide.mImag)/denom;
                imag = (leftSide.mImag*temp - leftSide.mReal)/denom;
            }
            return new Complex32(real, imag);
        }

        /// <summary>Divides this complex number by another one.</summary>
        /// <returns>The result of the division.</returns>
        /// <param name="other">The complex number to divide this one with.</param>
        public Complex32 Divide(Complex32 other)
        {
            return this/other;
        }

        ///<summary>Tests whether the the Complex32 number is not a number.</summary>
        ///<returns>True if either the real or imaginary components are NaN, false otherwise.</returns>
        public static bool IsNaN(Complex32 value)
        {
            if (value == sNaN)
            {
                return true;
            }
            return (float.IsNaN(value.mReal) || float.IsNaN(value.mImag));
        }

        ///<summary>Tests whether the the Complex32 number is infinite.</summary>
        ///<returns>True if either the real or imaginary components are infinite, false otherwise.</returns>
        public static bool IsInfinity(Complex32 value)
        {
            if (value == sInfinity)
            {
                return true;
            }
            return (float.IsInfinity(value.mReal) || float.IsInfinity(value.mImag));
        }

        /// <summary>A string representation of this Complex32 number.</summary>
        /// <returns>The string representation of this Complex32 number.</returns>
        public override string ToString()
        {
            return ToString(null, null);
        }

        /// <summary>A string representation of this Complex32 number.</summary>
        /// <returns>
        /// The string representation of this Complex32 number formatted as specified by the
        /// format string.
        /// </returns>
        /// <param name="format">A format specification.</param>
        public string ToString(string format)
        {
            return ToString(format, null);
        }

        /// <summary>A string representation of this Complex32 number.</summary>
        /// <returns>
        /// The string representation of this Complex32 number formatted as specified by the
        /// format provider.
        /// </returns>
        /// <param name="formatProvider">An IFormatProvider that supplies culture-specific formatting information.</param>
        public string ToString(IFormatProvider formatProvider)
        {
            return ToString(null, formatProvider);
        }

        /// <summary>
        /// Creates a Complex32 number based on a string. The string can be in the following
        /// formats(without the quotes): 'n', 'ni', 'n +/- ni', 'n,n', 'n,ni,' '(n,n)', or
        /// '(n,ni)', where n is a real number.
        /// </summary>
        /// <returns>A Complex32 number containing the value specified by the given string.</returns>
        /// <param name="value">The string to parse.</param>
        public static Complex32 Parse(String value)
        {
            return Parse(value, null);
        }

        /// <summary>
        /// Creates a Complex32 number based on a string. The string can be in the following
        /// formats(without the quotes): 'n', 'ni', 'n +/- ni', 'n,n', 'n,ni,' '(n,n)', or
        /// '(n,ni)', where n is a real number.
        /// </summary>
        /// <returns>A Complex32 number containing the value specified by the given string.</returns>
        /// <param name="value">the string to parse.</param>
        /// <param name="formatProvider">An IFormatProvider that supplies culture-specific formatting information.</param>
        public static Complex32 Parse(String value, IFormatProvider formatProvider)
        {
            if (value == null)
            {
                throw new ArgumentNullException(value);
            }
            value = value.Trim();
            if (value.Length == 0)
            {
                throw new FormatException();
            }

            //check if one character strings are valid
            if (value.Length == 1)
            {
                if (String.Compare(value, "i", StringComparison.OrdinalIgnoreCase) == 0)
                {
                    return new Complex32(0, 1);
                }
                return new Complex32(float.Parse(value, formatProvider));
            }

            //strip out parens
            if (value.StartsWith("(", StringComparison.Ordinal))
            {
                if (!value.EndsWith(")", StringComparison.Ordinal))
                {
                    throw new FormatException();
                }
                value = value.Substring(1, value.Length - 2);
            }

            string real = value;
            string imag = "0";

            //comma separated
            int index = value.IndexOf(',');
            if (index > -1)
            {
                real = value.Substring(0, index);
                imag = value.Substring(index + 1, value.Length - index - 1);
            }
            else
            {
                index = value.IndexOf('+', 1);
                if (index > -1)
                {
                    real = value.Substring(0, index);
                    imag = value.Substring(index + 1, value.Length - index - 1);
                }
                else
                {
                    index = value.IndexOf('-', 1);
                    if (index > -1)
                    {
                        real = value.Substring(0, index);
                        imag = value.Substring(index, value.Length - index);
                    }
                }
            }

            //see if we have numbers in the format xxxi
            if (real.EndsWith("i", StringComparison.OrdinalIgnoreCase))
            {
                if (!imag.Equals("0", StringComparison.Ordinal))
                {
                    throw new FormatException();
                }
                imag = real.Substring(0, real.Length - 1);
                real = "0";
            }
            if (imag.EndsWith("i", StringComparison.OrdinalIgnoreCase))
            {
                imag = imag.Substring(0, imag.Length - 1);
            }
            //handle cases of - n, + n
            if (real.StartsWith("-", StringComparison.Ordinal))
            {
                real = "-" + real.Substring(1, real.Length - 1).Trim();
            }
            if (imag.StartsWith("-", StringComparison.Ordinal))
            {
                imag = "-" + imag.Substring(1, imag.Length - 1).Trim();
            }

            Complex32 ret;
            try
            {
                ret = new Complex32(float.Parse(real.Trim(), formatProvider), float.Parse(imag.Trim(), formatProvider));
            }
            catch (Exception)
            {
                throw new FormatException();
            }
            return ret;
        }

        /// <summary>
        /// Converts the string representation of a complex number to a single-precision complex number equivalent. 
        /// A return value indicates whether the conversion succeeded or failed.
        /// </summary>
        /// <param name="value">A string containing a complex number to convert. </param>
        /// <param name="result"></param>
        /// <returns>If the conversion succeeds, the result will contain a complex number equivalent to value. 
        /// Otherwise the result will contain complex32.Zero.  This parameter is passed uninitialized</returns>
        public static bool TryParse(String value, out Complex32 result)
        {
            return TryParse(value, null, out result);
        }

        /// <summary>
        /// Converts the string representation of a complex number to a single-precision complex number equivalent. 
        /// A return value indicates whether the conversion succeeded or failed.
        /// </summary>
        /// <param name="value">A string containing a complex number to convert. </param>
        /// <param name="formatProvider">An IFormatProvider that supplies culture-specific formatting information about value. </param>
        /// <param name="result"></param>
        /// <returns>If the conversion succeeds, the result will contain a complex number equivalent to value. 
        /// Otherwise the result will contain complex32.Zero.  This parameter is passed uninitialized</returns>
        public static bool TryParse(String value, IFormatProvider formatProvider, out Complex32 result)
        {
            bool ret;
            try
            {
                result = Parse(value, formatProvider);
                ret = true;
            }
            catch (ArgumentNullException)
            {
                result = sZero;
                ret = false;
            }
            catch (FormatException)
            {
                result = sZero;
                ret = false;
            }
            return ret;
        }
    }
}